package com.jdjc.auth.domin.service.impl;

import cn.dev33.satoken.secure.SaSecureUtil;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import com.google.gson.Gson;
import com.jdjc.auth.common.enums.AuthUserStatusEnum;
import com.jdjc.auth.domin.constants.AuthConstants;
import com.jdjc.auth.domin.convert.AuthPermissionDominConverter;
import com.jdjc.auth.domin.convert.AuthUserDominConverter;
import com.jdjc.auth.domin.entity.AuthUserBO;
import com.jdjc.auth.domin.redis.RedisUtil;
import com.jdjc.auth.domin.service.AuthUserDominService;
import com.jdjc.auth.common.enums.IsDeletedFlagEnum;
import com.jdjc.auth.infra.basic.entity.*;
import com.jdjc.auth.infra.basic.service.*;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

@Service("AuthUserDominService")
@Slf4j
public class AuthUserDominServiceImpl implements AuthUserDominService {
    @Resource
    private AuthUserService authUserService;
    @Resource
    private AuthUserRoleService authUserRoleService;
    @Resource
    private AuthRoleService authRoleService;
    @Resource
    private AuthPermissionService authPermissionService;
    @Resource
    private AuthRolePermissionService authRolePermissionService;
    @Resource
    private RedisUtil redisUtil;

    private String authPermissionPrefix = "auth.permission";  // 定义权限前缀，用于构建Redis的键。
    private String authRolePrefix = "auth.role";  // 定义角色前缀
    private static final String LOGIN_PREFIX = "loginCode";

    private String salt = "wzm";

    //以编程方式控制事务的开始和结束。
    private TransactionTemplate transactionTemplate;

    /**
     * 注册用户
     *
     * @param authUserBO
     */
    @Override
    @SneakyThrows//使用Lombok注解抛出异常
    @Transactional(rollbackFor = Exception.class)
    public Boolean register(AuthUserBO authUserBO) {
        if (log.isInfoEnabled()) {
            log.info(" authUserBO：register:BO:{}", authUserBO);
        }
        //校验用户是否存在
        AuthUser exitsAuthUser=new AuthUser();
        exitsAuthUser.setUserName(authUserBO.getUserName());
        List<AuthUser> exitCount=authUserService.queryByCondition(exitsAuthUser);
        if (exitCount!=null && exitCount.size()>0){
            return true;
        }

        AuthUser authUser = AuthUserDominConverter.INSTANCE.convertBOToEntity(authUserBO);
        if (StringUtils.isNotBlank(authUser.getPassword())) {
            //密码不为空时，对用户密码进行加盐MD5加密
            authUser.setPassword(SaSecureUtil.md5BySalt(authUser.getPassword(), salt));
        }
        authUser.setStatus(AuthUserStatusEnum.OPEN.getCode());
        authUser.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        authUser.setAvatar("0");
        int count = authUserService.insert(authUser);

        //建立一个初步的角色关联,
        AuthRole authRole = new AuthRole();
        authRole.setRoleKey(AuthConstants.NORMAL_USER);
        AuthRole roleResult = authRoleService.queryByCondition(authRole);
        Long roleId = roleResult.getId();
        Long userId = authUser.getId();
        AuthUserRole authUserRole = new AuthUserRole();
        authUserRole.setRoleId(roleId);
        authUserRole.setUserId(userId);
        authUserRole.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        //关联为普通用户
        authUserRoleService.insert(authUserRole);

        // 将当前用户的角色和权限都存储到 Redis 中
        // 构建角色键名，结合特定的前缀和用户名，例如auth.role.鸡翅
        String roleKey = redisUtil.buildKey(authRolePrefix, authUser.getUserName());
        // 创建一个角色列表，并添加当前用户的角色
        List<AuthRole> roleList = new LinkedList<>();
        roleList.add(authRole);
        // 将角色列表转换为 JSON 字符串，并存储到 Redis 中
        redisUtil.set(roleKey, new Gson().toJson(roleList));

        // 创建一个角色权限对象，设置角色 ID
        AuthRolePermission authRolePermission = new AuthRolePermission();
        authRolePermission.setRoleId(roleId);
        // 查询符合条件的所有角色权限
        List<AuthRolePermission> rolePermissionList = authRolePermissionService.queryByCondition(authRolePermission);
        // 从角色权限列表中提取权限 ID 列表
        List<Long> permissionIdList = rolePermissionList.stream()
                .map(AuthRolePermission::getPermissionId)
                .collect(Collectors.toList());
        // 根据权限 ID 列表查询对应的权限信息
        List<AuthPermission> permissionList = authPermissionService.queryByRoleList(permissionIdList);
        // 构建权限键名，结合特定的前缀和用户名，例如auth.permission.鸡翅
        String permissionKey = redisUtil.buildKey(authPermissionPrefix, authUser.getUserName());
        // 将权限列表转换为 JSON 字符串，并存储到 Redis 中
        redisUtil.set(permissionKey, new Gson().toJson(permissionList));

        return count > 0;

       /* //对用户密码进行RSA加密,生成一个包含公钥和私钥的HashMap
        HashMap<String, String> keyMap = SaSecureUtil.rsaGenerateKeyPair();
        //从HashMap中获取公钥和私钥。
        String publicKey = keyMap.get("public");
        String privateKey = keyMap.get("private");
        //使用公钥加密密码
        authUser.setPassword(SaSecureUtil.rsaEncryptByPublic( publicKey,authUser.getPassword()));
        //私钥解密
        SaSecureUtil.rsaDecryptByPrivate(privateKey,authUser.getPassword());*/
        //对用户密码进行AES加密(对称加密）
        //authUser.setPassword(SaSecureUtil.aesEncrypt(authUser.getPassword(),key));

    }

    /**
     * 更新用户信息
     *
     * @param authUserBO
     * @return
     */
    @Override
    public Boolean update(AuthUserBO authUserBO) {
        if (log.isInfoEnabled()) {
            log.info(" authUserBO：update:BO:{}", authUserBO);
        }
        AuthUser authUser = AuthUserDominConverter.INSTANCE.convertBOToEntity(authUserBO);
        authUser.setStatus(AuthUserStatusEnum.OPEN.getCode());
        authUser.setIsDeleted(IsDeletedFlagEnum.UN_DELETED.getCode());
        int count = authUserService.updateByUserName(authUser);
        return count > 0;
    }

    /**
     * 删除用户
     *
     * @param authUserBO
     * @return
     */
    @Override
    public Boolean delete(AuthUserBO authUserBO) {
        AuthUser authUser = new AuthUser();
        authUser.setId(authUserBO.getId());
        authUser.setIsDeleted(IsDeletedFlagEnum.DELETED.getCode());
        //逻辑删除
        int count = authUserService.update(authUser);

        //TODO 有任何的更新都要和缓存进行同步
        return count > 0;
    }

    @Override
    public Boolean changeStatus(AuthUserBO authUserBO) {
        if (log.isInfoEnabled()) {
            log.info(" authUserBO：update:BO:{}", authUserBO);
        }
        AuthUser authUser = AuthUserDominConverter.INSTANCE.convertBOToEntity(authUserBO);
        int count = authUserService.update(authUser);
        //TODO 有任何的更新都要和缓存进行同步
        return count > 0;
    }

    /**
     * 查询用户信息
     *
     * @param authUserBO
     * @return
     */
    @Override
    public AuthUserBO getUserInfo(AuthUserBO authUserBO) {
        if (log.isInfoEnabled()) {
            log.info(" authUserBO：getUserInfo:BO:{}", authUserBO);
        }
        AuthUser authUser = AuthUserDominConverter.INSTANCE.convertBOToEntity(authUserBO);
        List<AuthUser> authUsers = authUserService.queryByCondition(authUser);
        if (CollectionUtils.isEmpty(authUsers)){
            return new AuthUserBO();
        }
        AuthUser user = authUsers.get(0);

        return  AuthUserDominConverter.INSTANCE.convertEntityToBO(user);

    }

    /**
     * 验证码登录
     *
     * @param validCode
     * @return
     */
    @Override
    public SaTokenInfo doLogin(String validCode) {
        //拿到redis的键值loginCode.验证码
        String loginKey = redisUtil.buildKey(LOGIN_PREFIX, validCode);
        //根据键值拿到value值，也就是微信用户的唯一标识OpneId（fromUserName）
        String openId = redisUtil.get(loginKey);
        if (StringUtils.isBlank(openId)) {
            return null;
        }

        AuthUserBO authUserBO = new AuthUserBO();
        authUserBO.setUserName(openId);
        this.register(authUserBO);

        // 使用 openId 登录用户，这将创建一个登录会话并生成一个令牌
        StpUtil.login(openId);
        // 获取当前登录会话的令牌信息
        // SaTokenInfo 类包含了令牌的相关信息，如令牌值、创建时间和过期时间
        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();

        return tokenInfo;
    }
}
